In [3]:
#!conda install -c conda-forge folium=0.5.0 --yes
#import folium

#print('Folium installed and imported!')
In [2]:
import numpy as np  # scientific computation
import pandas as pd # primary data structure library
import matplotlib.pyplot as plt #primary plotting stricture
import folium # map generator.

Puntos de acceso gratuitos de WiFi en Ciudad de México

En esta página se muestra el procesamiento de la información correspondiente a los AP de WiFi gratuitos en la ciudad de México. El fin de esta página es crear herramientas visuales que muestren la conectividad en la ciudad, para evaluar y justificar el proyecto de Movilidad Inteligente: conectividad vehicular, a realizar por alumnos de posgrado y pregado del Tecnológico de Monterrey.

A continuación se muestra el proceso de procesamiento y visualización:

Importación del archivo de puntos de acceso gratuitos

In [3]:
df = pd.read_csv('ubicacion-acceso-gratuito-internet-wifi-c5.csv') 
df.head(3)
Out[3]:
DIRECCIÓN ESQUINA COLONIA ALCALDIA BOTON ALTAVOZ LONGITUD LATITUD geopoint TIPO DE POSTE ESTATUS CONECTIVIDAD ESTADO DE CONECTIVIDAD INCREMENTO 100 Mbps
0 AV. JUAREZ ENTRE C. JOSE AZUETA Y C. LUIS MOYA CENTRO CUAUHTEMOC SIN BOTON CON ALTAVOZ -99.145820 19.435098 19.435098,-99.14582 9m WIFI GRATUITO_ACTIVO WIFI GRATUITO_ACTIVO SI
1 AV. JUAREZ ENTRE C. JOSE MARIA MARROQUI Y C. LUIS MOYA CENTRO CUAUHTEMOC CON BOTON CON ALTAVOZ -99.143858 19.434573 19.434573,-99.143858 9m WIFI GRATUITO_ACTIVO WIFI GRATUITO_ACTIVO SI
2 AV. INDEPENDENCIA C. JOSE AZUETA CENTRO CUAUHTEMOC CON BOTON CON ALTAVOZ -99.146751 19.433729 19.433729,-99.146751 9m WIFI GRATUITO_ACTIVO WIFI GRATUITO_ACTIVO SI

Definición del tipo de variables dentro del DataFrame

En este caso, primero se convierte todos los nombres de las columnas en cadenas de caracteres, en caso de que existieran números. Con el fin de homogeneizar la información


In [4]:
df.columns = list(map(str, df.columns))

df.dtypes
Out[4]:
DIRECCIÓN                  object
ESQUINA                    object
COLONIA                    object
ALCALDIA                   object
BOTON                      object
ALTAVOZ                    object
LONGITUD                  float64
LATITUD                   float64
geopoint                   object
TIPO DE POSTE              object
ESTATUS CONECTIVIDAD       object
ESTADO DE CONECTIVIDAD     object
INCREMENTO 100 Mbps        object
dtype: object

Filtrado de la base

In [5]:
df.drop(['BOTON', 'ALTAVOZ', 'ESQUINA'], axis = 1, inplace = True)
df.describe(include=['object'])
Out[5]:
DIRECCIÓN COLONIA ALCALDIA geopoint TIPO DE POSTE ESTATUS CONECTIVIDAD ESTADO DE CONECTIVIDAD INCREMENTO 100 Mbps
count 13694 13693 13694 13694 13694 13694 13694 13694
unique 7875 2865 16 13694 2 2 1 2
top EJE CENTRAL LAZARO CARDENAS CENTRO IZTAPALAPA 19.335706,-99.076306 9m WIFI GRATUITO_ACTIVO WIFI GRATUITO_ACTIVO SI
freq 58 286 2100 1 13464 13499 13694 11162

Esta descripción general nos da la forma general en la cual se tienen a las antenas de Wi Fi de la cdmx

Podemos ver que hay:

  • 16 Alcaldías
  • 2866 colonias
  • 2 Tipos de poste (la gran mayoría de 9 metros)
  • La gran mayoría es WIFI GRATUITO ACTIVO

El paso siguiente es limpiar el dataframe, donde no haya WIFI Activo. También deependiendo de si tiene un incremento de 100Mbps será un mayor alcance, se puede hacer un ajuste posterior.

In [6]:
df['ESTATUS CONECTIVIDAD'].unique()
Out[6]:
array(['WIFI GRATUITO_ACTIVO', 'REPORTE DE FALLA'], dtype=object)

Eliminación del dataframe aquellas antenas con reporte de falla

In [8]:
#df.drop(['REPORTE DE FALLA'], axis = 0).describe(include=['object'])
#df['ESTATUS CONECTIVIDAD'].replace('REPORTE DE FALLA', np.nan )


df.dropna(axis=0, inplace=True)
# reset index, because we droped two rows
df.reset_index(drop=True, inplace=True)
df
#Se eliminaron 200 antenas de Wi Fi con reporte de fallo aquí
Out[8]:
DIRECCIÓN COLONIA ALCALDIA LONGITUD LATITUD geopoint TIPO DE POSTE ESTATUS CONECTIVIDAD ESTADO DE CONECTIVIDAD INCREMENTO 100 Mbps
0 AV. JUAREZ CENTRO CUAUHTEMOC -99.145820 19.435098 19.435098,-99.14582 9m WIFI GRATUITO_ACTIVO WIFI GRATUITO_ACTIVO SI
1 AV. JUAREZ CENTRO CUAUHTEMOC -99.143858 19.434573 19.434573,-99.143858 9m WIFI GRATUITO_ACTIVO WIFI GRATUITO_ACTIVO SI
2 AV. INDEPENDENCIA CENTRO CUAUHTEMOC -99.146751 19.433729 19.433729,-99.146751 9m WIFI GRATUITO_ACTIVO WIFI GRATUITO_ACTIVO SI
3 C. INDEPENDENCIA CENTRO CUAUHTEMOC -99.142696 19.433091 19.433091,-99.142696 9m WIFI GRATUITO_ACTIVO WIFI GRATUITO_ACTIVO SI
4 AV. DEL CASTILLO COCOYOTES GUSTAVO A. MADERO -99.133984 19.548495 19.548495,-99.133984 9m WIFI GRATUITO_ACTIVO WIFI GRATUITO_ACTIVO SI
... ... ... ... ... ... ... ... ... ... ...
13688 LAGO CANEGUIN ARGENTINA ANTIGUA MIGUEL HIDALGO -99.208965 19.455852 19.455852,-99.208965 9m WIFI GRATUITO_ACTIVO WIFI GRATUITO_ACTIVO NO
13689 C. GOB. LUIS GONZAGA VIEYRA SAN MIGUEL CHAPULTEPEC MIGUEL HIDALGO -99.187815 19.406992 19.406992,-99.187815 9m WIFI GRATUITO_ACTIVO WIFI GRATUITO_ACTIVO SI
13690 AV 4 PUEBLA VENUSTIANO CARRANZA -99.079684 19.408708 19.408708,-99.079684 9m WIFI GRATUITO_ACTIVO WIFI GRATUITO_ACTIVO SI
13691 DAMASCO SIMON BOLIVAR VENUSTIANO CARRANZA -99.096327 19.447542 19.447542,-99.096327 9m WIFI GRATUITO_ACTIVO WIFI GRATUITO_ACTIVO SI
13692 VIADUCTO RIO DE LA PIEDAD IGNACIO ZARAGOZA VENUSTIANO CARRANZA -99.092237 19.408801 19.408801,-99.092237 9m WIFI GRATUITO_ACTIVO WIFI GRATUITO_ACTIVO SI

13693 rows × 10 columns

A continuación se crea un nuevo dataframe que agrupe el número de antenas por colonia

In [9]:
dfCol = df.groupby(['COLONIA']).count().rename(columns={"DIRECCIÓN":"DIR"}).reset_index()
dfCol.head()
Out[9]:
COLONIA DIR ALCALDIA LONGITUD LATITUD geopoint TIPO DE POSTE ESTATUS CONECTIVIDAD ESTADO DE CONECTIVIDAD INCREMENTO 100 Mbps
0 10 DE ABRIL 1 1 1 1 1 1 1 1 1
1 10 DE MAYO 6 6 6 6 6 6 6 6 6
2 12 DE DICIEMBRE 1 1 1 1 1 1 1 1 1
3 15 DE AGOSTO 4 4 4 4 4 4 4 4 4
4 16 DE SEPTIEMBRE 9 9 9 9 9 9 9 9 9

Sección 2: Herramientas de visualización

El siguiente paso, una vez filtrada nuestra base de datos es hacer visualización geoespacial. Para lograr esto, se recurre al archivo geojson de la ciudad de México, disponible en:

Mapa coropléxico de WiFi en CDMX por colonia

In [10]:
cdmx_geo = r'coloniascdmx.geojson'

# creating a numpy array of length 6 and has linear spacing from the minium total immigration to the maximum total immigration
threshold_scale = np.linspace(dfCol['DIR'].min(),
                              (dfCol['DIR'].max()-200),
                              6, dtype=int)
threshold_scale = threshold_scale.tolist() # change the numpy array to a list
threshold_scale[-1] = threshold_scale[-1] + 1 # make sure that the last value of the list is greater than the maximum immigration

mapa_cdmxCol = folium.Map(location=[19.4284700 , -99.1276600], zoom_start=12, tiles='Mapbox Bright')
mapa_cdmxCol.choropleth(
    geo_data=cdmx_geo,
    data=dfCol,
    columns=['COLONIA', 'DIR'],
    key_on='feature.properties.nombre',
    threshold_scale=threshold_scale,
    fill_color='BuPu', 
    fill_opacity=0.7, 
    line_opacity=0.2,
    legend_name='Mapa Coroplexico WiFi Gratuito CDMX por Colonias',
    reset=True
)
mapa_cdmxCol
Out[10]:

Mapa coropléxico de conectividad WiFi CDMX por alcaldía

In [11]:
dfAlc= df.groupby(['ALCALDIA']).count().rename(columns={"DIRECCIÓN":"DIR"}).reset_index()
dfAlc.head()
Out[11]:
ALCALDIA DIR COLONIA LONGITUD LATITUD geopoint TIPO DE POSTE ESTATUS CONECTIVIDAD ESTADO DE CONECTIVIDAD INCREMENTO 100 Mbps
0 ALVARO OBREGON 899 899 899 899 899 899 899 899 899
1 AZCAPOTZALCO 707 707 707 707 707 707 707 707 707
2 BENITO JUAREZ 796 796 796 796 796 796 796 796 796
3 COYOACAN 907 907 907 907 907 907 907 907 907
4 CUAJIMALPA 239 239 239 239 239 239 239 239 239
In [12]:
cdmx_geo = r'coloniascdmx.geojson'

mapa_cdmxAlc = folium.Map(location=[19.4284700 , -99.1276600], zoom_start=12,tiles='Mapbox Bright')
mapa_cdmxAlc.choropleth(
    geo_data=cdmx_geo,
    data=dfAlc,
    columns=['ALCALDIA', 'DIR'],
    key_on='feature.properties.alcaldia',
    #threshold_scale=threshold_scale,
    fill_color='BuPu', 
    fill_opacity=0.7, 
    line_opacity=0.5,
    legend_name='Mapa Coroplexico WiFi Gratuito CDMX por Alcaldías',
    reset=True
)
mapa_cdmxAlc
Out[12]:

Creación de mapa interactivo para localización de Antenas

In [14]:
nummap = df.rename(columns={"LONGITUD":"x", "LATITUD":"y"}).drop(['TIPO DE POSTE'], axis=1)
nummap.head()
Out[14]:
DIRECCIÓN COLONIA ALCALDIA x y geopoint ESTATUS CONECTIVIDAD ESTADO DE CONECTIVIDAD INCREMENTO 100 Mbps
0 AV. JUAREZ CENTRO CUAUHTEMOC -99.145820 19.435098 19.435098,-99.14582 WIFI GRATUITO_ACTIVO WIFI GRATUITO_ACTIVO SI
1 AV. JUAREZ CENTRO CUAUHTEMOC -99.143858 19.434573 19.434573,-99.143858 WIFI GRATUITO_ACTIVO WIFI GRATUITO_ACTIVO SI
2 AV. INDEPENDENCIA CENTRO CUAUHTEMOC -99.146751 19.433729 19.433729,-99.146751 WIFI GRATUITO_ACTIVO WIFI GRATUITO_ACTIVO SI
3 C. INDEPENDENCIA CENTRO CUAUHTEMOC -99.142696 19.433091 19.433091,-99.142696 WIFI GRATUITO_ACTIVO WIFI GRATUITO_ACTIVO SI
4 AV. DEL CASTILLO COCOYOTES GUSTAVO A. MADERO -99.133984 19.548495 19.548495,-99.133984 WIFI GRATUITO_ACTIVO WIFI GRATUITO_ACTIVO SI
In [15]:
from folium import plugins
latitude=19.4284700 
longitude=-99.1276600
# let's start again with a clean copy of the map of San Francisco
map_cdmx_num = folium.Map(location = [latitude, longitude], zoom_start = 12)

# instantiate a mark cluster object for the incidents in the dataframe
incidents = plugins.MarkerCluster().add_to(map_cdmx_num)

# loop through the dataframe and add each data point to the mark cluster
for lat, lng, label, in zip(nummap['y'], nummap['x'], nummap['ESTATUS CONECTIVIDAD']):
    folium.Marker(
        location=[lat, lng],
        icon=None,
        popup=label,
    ).add_to(incidents)

# display map
map_cdmx_num
Out[15]:

Haciendo un análisis más profundo, se puede encontrar un factor de probabilidad de enlace por colonia en cdmx, suponiendo una distribución eficiente de las antenas y una posición aleatoria del auto dentro de la colonia.

Para esto hacemos un cálculo de la superficie de cada colonia en la cdmx

In [2]:
#pip install area
Collecting area
  Downloading https://files.pythonhosted.org/packages/f4/1b/9e434947304bafaeca6a5760272ded9b43edbcadfe9f15116596b4d6b7eb/area-1.1.1.tar.gz
Building wheels for collected packages: area
  Building wheel for area (setup.py): started
  Building wheel for area (setup.py): finished with status 'done'
  Created wheel for area: filename=area-1.1.1-cp37-none-any.whl size=3614 sha256=5ad7cb91f76b2ec500187f0fe85a00645e27492f91da19f93152d169ff8811f3
  Stored in directory: C:\Users\alecb\AppData\Local\pip\Cache\wheels\66\c0\12\b2ef12e07a617ba6f7aaeb32348ea6f14205cc1985f51a134e
Successfully built area
Installing collected packages: area
Successfully installed area-1.1.1
Note: you may need to restart the kernel to use updated packages.
In [16]:
from area import area
#obj = {'type':'Polygon','coordinates':[[[-180,-90],[-180,90],[180,90],[180,-90],[-180,-90]]]}
#area(obj)
Out[16]:
511207893395811.06
In [53]:
colAr= pd.read_csv('coloniascdmx.csv')
colAr.describe(include=['object'])
Out[53]:
COLONIA Geo Point Geo Shape ALCALDIA CVE_COL SECC_COM SECC_PAR
count 1812 1808 1808 1812 1812 1132 1543
unique 1743 1808 1808 16 1812 1132 1358
top LA JOYA 19.3698194807,-99.0698013824 {"type": "Polygon", "coordinates": [[[-99.1609... IZTAPALAPA 11-024 2122, 2124, 2322, 2323, 2324, 2325 3491
freq 4 1 1 293 1 1 9
In [54]:
#colAr.shape
colAr.replace(" ", np.nan, inplace = True)
colAr.dropna(subset=["Geo Shape"], axis=0, inplace=True)
colAr.drop(['ENTIDAD', 'CVE_COL', 'SECC_COM', 'SECC_PAR'], axis = 1, inplace = True)
colonias = colAr
colonias[~colonias['Geo Shape'].isnull()] 
Out[54]:
COLONIA Geo Point Geo Shape CVE_ALC ALCALDIA
0 IRRIGACION 19.4429549298,-99.2099357048 {"type": "Polygon", "coordinates": [[[-99.2115... 16 MIGUEL HIDALGO
1 MARINA NACIONAL (U HAB) 19.4466319056,-99.1795110575 {"type": "Polygon", "coordinates": [[[-99.1797... 16 MIGUEL HIDALGO
2 PEDREGAL DE STO DOMINGO VI 19.3234027183,-99.1654676133 {"type": "Polygon", "coordinates": [[[-99.1622... 3 COYOACAN
3 VILLA PANAMERICANA 7MA. SECCIN (U HAB) 19.304604269,-99.1677617231 {"type": "Polygon", "coordinates": [[[-99.1676... 3 COYOACAN
4 VILLA PANAMERICANA 6TA. SECCIN (U HAB) 19.3112238873,-99.1696478642 {"type": "Polygon", "coordinates": [[[-99.1702... 3 COYOACAN
... ... ... ... ... ...
1807 ROMERO DE TERREROS (FRACC) 19.3419952581,-99.176673437 {"type": "Polygon", "coordinates": [[[-99.1758... 3 COYOACAN
1808 VILLA PANAMERICANA 2DA. SECCIN (U HAB) 19.3064020438,-99.1762091981 {"type": "Polygon", "coordinates": [[[-99.1783... 3 COYOACAN
1809 VILLA PANAMERICANA 3ERA. SECCIN (U HAB) 19.308172228,-99.1720563222 {"type": "Polygon", "coordinates": [[[-99.1736... 3 COYOACAN
1810 EX EJIDO DE CHURUBUSCO 19.3399880918,-99.130307686 {"type": "Polygon", "coordinates": [[[-99.1303... 3 COYOACAN
1811 7 DE JULIO 19.4316039931,-99.1126775669 {"type": "Polygon", "coordinates": [[[-99.1153... 17 VENUSTIANO CARRANZA

1808 rows × 5 columns

In [55]:
colonias.shape
Out[55]:
(1808, 5)
In [56]:
colfiltered = colonias.drop([colonias.index[279] , colonias.index[397]])
colfiltered.shape
Out[56]:
(1806, 5)
In [57]:
colfiltered.reset_index(drop=True, inplace=True)
a = []
for i in range(0, 1806):
    a.append(area(colfiltered['Geo Shape'][i]))
In [58]:
arr = np.array(a)
arr
Out[58]:
array([976562.75352115,  43407.1550837 , 343349.97554104, ...,
       118399.05867127,   2499.06278559, 404224.01302042])
In [59]:
colfiltered['area'] = arr
colfiltered.shape
Out[59]:
(1806, 6)
In [60]:
area(colfiltered['Geo Shape'][2])
Out[60]:
343349.97554104
In [61]:
alcald =colfiltered.groupby(['ALCALDIA']).sum().reset_index()
#alcald.area
#alcald
dfAlc['Area']= alcald.area
dfAlc
Out[61]:
ALCALDIA DIR COLONIA LONGITUD LATITUD geopoint TIPO DE POSTE ESTATUS CONECTIVIDAD ESTADO DE CONECTIVIDAD INCREMENTO 100 Mbps area Area
0 ALVARO OBREGON 899 899 899 899 899 899 899 899 899 7.148793e+07 7.148793e+07
1 AZCAPOTZALCO 707 707 707 707 707 707 707 707 707 3.371803e+07 3.371803e+07
2 BENITO JUAREZ 796 796 796 796 796 796 796 796 796 2.680896e+07 2.680896e+07
3 COYOACAN 907 907 907 907 907 907 907 907 907 5.427962e+07 5.427962e+07
4 CUAJIMALPA 239 239 239 239 239 239 239 239 239 4.513810e+07 4.513810e+07
5 CUAUHTEMOC 1448 1448 1448 1448 1448 1448 1448 1448 1448 3.222015e+07 3.222015e+07
6 GUSTAVO A. MADERO 1790 1790 1790 1790 1790 1790 1790 1790 1790 8.703876e+07 8.703876e+07
7 IZTACALCO 664 664 664 664 664 664 664 664 664 2.325539e+07 2.325539e+07
8 IZTAPALAPA 2100 2100 2100 2100 2100 2100 2100 2100 2100 1.146980e+08 1.146980e+08
9 MAGDALENA CONTRERAS 305 305 305 305 305 305 305 305 305 2.330219e+07 2.330219e+07
10 MIGUEL HIDALGO 926 926 926 926 926 926 926 926 926 4.755527e+07 4.755527e+07
11 MILPA ALTA 213 213 213 213 213 213 213 213 213 4.027485e+07 4.027485e+07
12 TLAHUAC 540 540 540 540 540 540 540 540 540 6.382397e+07 6.382397e+07
13 TLALPAN 753 753 753 753 753 753 753 753 753 1.105062e+08 1.105062e+08
14 VENUSTIANO CARRANZA 990 990 990 990 990 990 990 990 990 3.410171e+07 3.410171e+07
15 XOCHIMILCO 416 416 416 416 416 416 416 416 416 1.077742e+08 1.077742e+08

Suponiendo un rango máximo de 80m con una propagación isotrópica bidimensional, basado en la documentación del protoxolo 802.11a/b/g/n se puede obtener la probabilidad de conexión de un automóvil en un punto cualquiera de la alcaldía. Asumiendo una correcta distribución de las antenas

In [89]:
prob = ((dfAlc['DIR'] * (3.1416*(6400) )) / dfAlc['Area'] )

for i in range(len(prob)):
    if(prob[i] >=1):
        prob[i] = 1
prob = np.array(prob)
prob
Out[89]:
array([0.25284702, 0.42158784, 0.59698567, 0.33597066, 0.10645975,
       0.903591  , 0.41349591, 0.57408388, 0.3681242 , 0.26316851,
       0.39151033, 0.10633508, 0.17011431, 0.13700591, 0.58370027,
       0.07760853])
In [92]:
dfAlc['Prob'] = prob* 100


dfAlc.head(16)
Out[92]:
ALCALDIA DIR COLONIA LONGITUD LATITUD geopoint TIPO DE POSTE ESTATUS CONECTIVIDAD ESTADO DE CONECTIVIDAD INCREMENTO 100 Mbps area Area Prob
0 ALVARO OBREGON 899 899 899 899 899 899 899 899 899 7.148793e+07 7.148793e+07 25.284702
1 AZCAPOTZALCO 707 707 707 707 707 707 707 707 707 3.371803e+07 3.371803e+07 42.158784
2 BENITO JUAREZ 796 796 796 796 796 796 796 796 796 2.680896e+07 2.680896e+07 59.698567
3 COYOACAN 907 907 907 907 907 907 907 907 907 5.427962e+07 5.427962e+07 33.597066
4 CUAJIMALPA 239 239 239 239 239 239 239 239 239 4.513810e+07 4.513810e+07 10.645975
5 CUAUHTEMOC 1448 1448 1448 1448 1448 1448 1448 1448 1448 3.222015e+07 3.222015e+07 90.359100
6 GUSTAVO A. MADERO 1790 1790 1790 1790 1790 1790 1790 1790 1790 8.703876e+07 8.703876e+07 41.349591
7 IZTACALCO 664 664 664 664 664 664 664 664 664 2.325539e+07 2.325539e+07 57.408388
8 IZTAPALAPA 2100 2100 2100 2100 2100 2100 2100 2100 2100 1.146980e+08 1.146980e+08 36.812420
9 MAGDALENA CONTRERAS 305 305 305 305 305 305 305 305 305 2.330219e+07 2.330219e+07 26.316851
10 MIGUEL HIDALGO 926 926 926 926 926 926 926 926 926 4.755527e+07 4.755527e+07 39.151033
11 MILPA ALTA 213 213 213 213 213 213 213 213 213 4.027485e+07 4.027485e+07 10.633508
12 TLAHUAC 540 540 540 540 540 540 540 540 540 6.382397e+07 6.382397e+07 17.011431
13 TLALPAN 753 753 753 753 753 753 753 753 753 1.105062e+08 1.105062e+08 13.700591
14 VENUSTIANO CARRANZA 990 990 990 990 990 990 990 990 990 3.410171e+07 3.410171e+07 58.370027
15 XOCHIMILCO 416 416 416 416 416 416 416 416 416 1.077742e+08 1.077742e+08 7.760853

Replicando el mismo mapa, pero ahora con probabilidad de cobertura

In [94]:
cdmx_geo = r'coloniascdmx.geojson'


mapa_cdmxAlc = folium.Map(location=[19.4284700 , -99.1276600], zoom_start=12,tiles='Mapbox Bright')
mapa_cdmxAlc.choropleth(
    geo_data=cdmx_geo,
    data=dfAlc,
    columns=['ALCALDIA', 'Prob'],
    key_on='feature.properties.alcaldia',
    threshold_scale=threshold_scale,
    fill_color='BuPu', 
    fill_opacity=0.7, 
    line_opacity=0.5,
    legend_name='Mapa Coroplexico Probabilidad conexion CDMX por Alcaldias',
    reset=True
)
mapa_cdmxAlc
Out[94]: